﻿/**************************************************************
 *
 * 唯一标识：aa4b634b-7d92-4455-95c5-6b6340296fe6
 * 命名空间：Sgr.BackGroundTasks
 * 创建时间：2023/8/31 15:46:53
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：Quartz通用任务执行器，负责执行所有注册的后台任务
 *
 **************************************************************/

using Microsoft.Extensions.Options;
using Quartz;
using Sgr.Utilities;

namespace Sgr.BackGroundTasks
{
    /// <summary>
    /// Quartz通用任务执行器，用于执行所有注册的后台任务
    /// </summary>
    public class QuartzGenericJob : IJob
    {
        private readonly BackGroundTaskOptions _backGroundTaskOptions;
        private readonly IServiceScopeFactory _serviceScopeFactory;
        private readonly IBackGroundTaskExecutor _backGroundTaskExecutor;
        private readonly IBackGroundExceptionHandle _backGroundExceptionHandle;
        private readonly ILogger<QuartzGenericJob> _logger;

        /// <summary>
        /// 初始化QuartzGenericJob的新实例
        /// </summary>
        /// <param name="backGroundTaskOptions">后台任务配置选项</param>
        /// <param name="serviceScopeFactory">服务范围工厂</param>
        /// <param name="backGroundTaskExecutor">后台任务执行器</param>
        /// <param name="backGroundExceptionHandle">后台任务异常处理程序</param>
        /// <param name="logger">日志记录器</param>
        public QuartzGenericJob(IOptions<BackGroundTaskOptions> backGroundTaskOptions, IServiceScopeFactory serviceScopeFactory, IBackGroundTaskExecutor backGroundTaskExecutor, IBackGroundExceptionHandle backGroundExceptionHandle, ILogger<QuartzGenericJob> logger)
        {
            _backGroundTaskOptions = backGroundTaskOptions?.Value ?? throw new ArgumentNullException(nameof(backGroundTaskOptions));
            _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
            _backGroundTaskExecutor = backGroundTaskExecutor ?? throw new ArgumentNullException(nameof(backGroundTaskExecutor));
            _backGroundExceptionHandle = backGroundExceptionHandle ?? throw new ArgumentNullException(nameof(backGroundExceptionHandle));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        /// <summary>
        /// 执行后台任务
        /// </summary>
        /// <param name="context">作业执行上下文</param>
        public async Task Execute(IJobExecutionContext context)
        {
            // 获取任务类型名称
            var taskClassTypeName = context.MergedJobDataMap.GetString(QuartzBackGroundTaskManager.KEY_TASK_CLASS_TYPE) ?? "null";

            // 获取任务元数据
            BackGroundTaskMetadata taskMetadata;
            try
            {
                taskMetadata = _backGroundTaskOptions.GetBackGroundTaskMetadata(taskClassTypeName)
                    ?? throw new BackGroundTaskException($"未注册任务类型：{taskClassTypeName}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取任务元数据失败");
                return;
            }

            // 检查任务是否已被取消
            if (context.CancellationToken.IsCancellationRequested)
            {
                _logger.LogWarning("任务在开始前已被取消: {TaskName}", taskMetadata.TaskName);
                return;
            }

            // 如果任务有输入参数，则解析参数
            object? inputData = null;
            string? rawData = null;
            if (taskMetadata.HasInputParameter)
            {
                //如果无法获取执行任务所需的数据，则退出
                rawData = context.MergedJobDataMap.GetString(QuartzBackGroundTaskManager.KEY_DATA_OBJECT);

                if (string.IsNullOrEmpty(rawData))
                {
                    _logger.LogError(
                             "任务执行失败: 无法获取任务数据 - TaskName: {TaskName}, TaskType: {TaskType}",
                             taskMetadata.TaskName,
                             taskMetadata.TaskClassType.Name);
                    return;
                }

                try
                {
                    inputData = JsonHelper.DeserializeObject(taskMetadata.TaskDataType!, rawData);
                }
                catch (Exception ex)
                {
                    _logger.LogError(
                          ex,
                          "任务执行失败: 任务数据反序列化失败 - TaskName: {TaskName}, Data: {Data}",
                          taskMetadata.TaskName,
                          rawData);
                }

                if (inputData == null)
                {
                    _logger.LogError(
                        "任务执行失败: 任务数据为空 - TaskName: {TaskName}, Data: {Data}",
                        taskMetadata.TaskName,
                        rawData);
                    return;
                }
            }

            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var backGroundTaskContext = new BackGroundTaskContext(
                    scope.ServiceProvider,
                    taskMetadata.TaskClassType,
                    inputData);

                try
                {
                    await _backGroundTaskExecutor.ExecuteAsync(backGroundTaskContext, context.CancellationToken);
                }
                catch (OperationCanceledException) when (context.CancellationToken.IsCancellationRequested)
                {
                    _logger.LogWarning("任务执行被取消: {TaskName}", taskMetadata.TaskName);
                }
                catch (Exception exception)
                {
                    await HandleTaskExecutionExceptionAsync(context, taskMetadata, rawData, backGroundTaskContext, exception);
                }
            }
        }

        /// <summary>
        /// 处理任务执行异常
        /// </summary>
        /// <param name="context">作业执行上下文</param>
        /// <param name="taskMetadata">任务元数据</param>
        /// <param name="rawData">原始任务数据</param>
        /// <param name="backGroundTaskContext">后台任务上下文</param>
        /// <param name="exception">捕获到的异常</param>
        /// <returns></returns>
        private async Task HandleTaskExecutionExceptionAsync(IJobExecutionContext context, BackGroundTaskMetadata taskMetadata, string? rawData, BackGroundTaskContext backGroundTaskContext, Exception exception)
        {
            _logger.LogError(exception,
                           "任务执行失败: {TaskName} - 重试次数: {RetryCount}",
                           taskMetadata.TaskName,
                           context.RefireCount);

            // 通知任务执行失败
            try
            {
                await _backGroundExceptionHandle.OnTaskFailAsync(backGroundTaskContext, context.RefireCount);
            }
            catch (Exception ex)
            {
                _logger.LogError(
                    ex,
                    "执行任务失败处理程序时发生异常: {TaskName}",
                    taskMetadata.TaskName);
            }

            // 获取和解析最大重试次数
            int maxRetryCount = ResolveMaxRetryCount(context);

            if (context.RefireCount > maxRetryCount)
            {
                await HandleTaskExceededMaxRetriesAsync(taskMetadata, rawData, backGroundTaskContext);
                return;
            }

            // 如果未超出最大重试次数，则安排下一次重试
            await ScheduleTaskRetryAsync(context);
        }

        /// <summary>
        /// 安排任务重试
        /// </summary>
        /// <param name="context">作业执行上下文</param>
        private async Task ScheduleTaskRetryAsync(IJobExecutionContext context)
        {
            try
            {
                // 获取和解析重试间隔
                int retryIntervalSecond = ResolveRetryInterval(context);

                // 创建重试触发器
                var oldTrigger = context.Trigger;
                var newTrigger = TriggerBuilder.Create()
                    .WithIdentity($"{oldTrigger.Key.Name}-retry-{Guid.NewGuid():N}", oldTrigger.Key.Group)
                    .StartAt(DateTimeOffset.UtcNow.AddSeconds(retryIntervalSecond))
                    .WithPriority(oldTrigger.Priority)
                    .UsingJobData(context.MergedJobDataMap)
                    .Build();

                // 安排重试
                await context.Scheduler.ScheduleJob(newTrigger);

                _logger.LogInformation(
                    "任务已安排重试: {TriggerName}, 将在 {RetryInterval} 秒后重试",
                    newTrigger.Key.Name,
                    retryIntervalSecond);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "安排任务重试时发生异常");
            }
        }

        /// <summary>
        /// 处理任务超过最大重试次数的情况
        /// </summary>
        /// <param name="taskMetadata">任务元数据</param>
        /// <param name="backGroundTaskContext">后台任务上下文</param>
        /// <param name="rawData">原始任务数据</param>
        private async Task HandleTaskExceededMaxRetriesAsync(BackGroundTaskMetadata taskMetadata, string? rawData, BackGroundTaskContext backGroundTaskContext)
        {
            //任务连续失败指定次数，则可认为任务将不可能成功,那么需要进行特殊处理
            _logger.LogWarning(
                "任务执行失败并达到最大重试次数: {TaskName}, 数据: {Data}",
                taskMetadata.TaskName,
                rawData ?? "无数据");
            try
            {
                await _backGroundExceptionHandle.OnTaskNeverSucceedAsync(backGroundTaskContext);

                _logger.LogInformation(
                    "已成功处理任务永远不会成功的情况: {TaskName}",
                    taskMetadata.TaskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(
                    ex,
                    "处理任务永远不会成功的情况时发生异常: {TaskName}",
                    taskMetadata.TaskName);
            }
        }

        /// <summary>
        /// 解析任务最大重试次数
        /// </summary>
        /// <param name="context">作业执行上下文</param>
        /// <returns>解析出的最大重试次数，如果未指定或无效则使用默认值</returns>
        private int ResolveMaxRetryCount(IJobExecutionContext context)
        {
            var strMaxRetryCount = context.MergedJobDataMap.GetString(QuartzBackGroundTaskManager.KEY_MAX_RETRY_COUNT);

            if (!string.IsNullOrEmpty(strMaxRetryCount) &&
                int.TryParse(strMaxRetryCount, out int maxRetryCount) &&
                maxRetryCount > 0)
            {
                return maxRetryCount;
            }

            return _backGroundTaskOptions.MaxRetryCountOnError;
        }

        /// <summary>
        /// 解析重试间隔
        /// </summary>
        /// <param name="context">作业执行上下文</param>
        /// <returns>解析出的重试间隔（秒），如果未指定或无效则使用默认值</returns>
        private int ResolveRetryInterval(IJobExecutionContext context)
        {
            var strRetryInterval = context.MergedJobDataMap.GetString(QuartzBackGroundTaskManager.KEY_RETRY_INTERVAL);

            if (!string.IsNullOrEmpty(strRetryInterval) &&
                int.TryParse(strRetryInterval, out int retryInterval) &&
                retryInterval > 0)
            {
                return retryInterval;
            }

            return _backGroundTaskOptions.RetryIntervalSecond;
        }
    }
}